// 版权所有20092010 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 达尔文系统调用。
// 该文件被编译为普通的Go代码，
// 但它也是mksyscall的输入，
// 它解析
// 请注意，有时我们使用小写
// syscall_bsd中。go或syscall_unix。去

package syscall

import (
	"internal/abi"
	"unsafe"
)

type SockaddrDatalink struct {
	Len    uint8
	Family uint8
	Index  uint16
	Type   uint8
	Nlen   uint8
	Alen   uint8
	Slen   uint8
	Data   [12]int8
	raw    RawSockaddrDatalink
}

// 将“kern.hostname”翻译成[]_C_int{0,1,2,3}。
func nametomib(name string) (mib []_C_int, err error) {
	const siz = unsafe.Sizeof(mib[0])

	// 注意（rsc）：将缓冲区设置为有
	// 大小CTL_MAXNAME+2，但仅使用CTL_MAXNAME 
	// 作为大小似乎很奇怪。我不知道为什么这里有+2，但是
	// 内核使用+2来实现这个函数。
	// 我担心，如果我们不在这里包含+2，内核
	// 将默默地比我们指定的
	// 多写两个字，我们将得到内存损坏。
	var buf [CTL_MAXNAME + 2]_C_int
	n := uintptr(CTL_MAXNAME) * siz

	p := (*byte)(unsafe.Pointer(&buf[0]))
	bytes, err := ByteSliceFromString(name)
	if err != nil {
		return nil, err
	}

	// Magic sysctl:“设置”0.3为字符串名
	// 允许您读回整数数组形式。
	if err = sysctl([]_C_int{0, 3}, p, &n, &bytes[0], uintptr(len(name))); err != nil {
		return nil, err
	}
	return buf[0 : n/siz], nil
}

func direntIno(buf []byte) (uint64, bool) {
	return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
}

func direntReclen(buf []byte) (uint64, bool) {
	return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
}

func direntNamlen(buf []byte) (uint64, bool) {
	return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
}

func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) }
func PtraceDetach(pid int) (err error) { return ptrace(PT_DETACH, pid, 0, 0) }

// sysnb pipe（p*[2]int32）（错误）

func Pipe(p []int) (err error) {
	if len(p) != 2 {
		return EINVAL
	}
	var q [2]int32
	err = pipe(&q)
	if err == nil {
		p[0] = int(q[0])
		p[1] = int(q[1])
	}
	return
}

func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
	var _p0 unsafe.Pointer
	var bufsize uintptr
	if len(buf) > 0 {
		_p0 = unsafe.Pointer(&buf[0])
		bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf))
	}
	r0, _, e1 := syscall(abi.FuncPCABI0(libc_getfsstat_trampoline), uintptr(_p0), bufsize, uintptr(flags))
	n = int(r0)
	if e1 != 0 {
		err = e1
	}
	return
}

func libc_getfsstat_trampoline()

// go:cgo_import_dynamic libc_getfsstat getfsstat/usr/lib/libSystem.B.dylib“

// sys utimensat（dirfd int，path string，times*[2]Timespec，flags int）（错误）

/*
 * Wrapped
 */

// sys kill（pid int，signum int，posix int）（错误）

func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1) }

/*
 * Exposed directly
 */
// sys-Chdir（路径字符串）（错误）
// sys-Chflags（路径字符串，标志int）（错误）
// sys-Chmod（路径字符串，模式uint32）（错误）
// sys Chown（路径字符串，uid int，gid int）（错误）
// sys closedir（dir uintpttr）（error）
// sys Dup（fd int）（nfd int，error error）
// sys Fchown（fd int，uid int，gid int）（err error）
// sys Flock（fd int，how int）（err err err error）
// sys Fpathconf（fd int，name int）（val int，err error）
// sys Fsync（fd int int）
// sys Ftruncate（fd int，length int64）（错误）
// sys Getdtablesize（）（size int）
// sysnb Getegid（）（egid int）
// sysnb Geteuid（）（uid int）
// sysnb Getppid（）（ppid int）
// sys Getpriority（who int，who int）（prio int，error error）
// sysnb Getrlimit（who int，lim*Rlimit）（error error）
// sys Kqueue（）（fd int，error）
// sys Lchown（路径字符串，uid int，gid int）（error）
// sys Link（路径字符串，链接字符串）（error error）
// sys Listen（s int，backlog int）（error）
// sys Mkdir（路径字符串，模式uint32）（error）
// sys Mknod（路径字符串，模式uint32，dev int）（错误）
// sys Mlock（b[]字节）（错误）
// sys Mlockall（flags int）（错误）
// sys Readlink（路径字符串，buf[]字节）（n int，error error）
// sys Rename（从字符串到字符串）（error error）
// sys Revoke（路径字符串）（error error）
// sys Rmdir（路径字符串）（error error）
// sys Seek（fd int，offset int64，whence int）（newoffset int64，error error）=sys Seek 
// sys Select（n int，r*FdSet，w*FdSet，e*FdSet，timeout*Timeval）（error error）
// sys Setegid（egid int）（error error）
// sysnb Seteuid（euid int）（error）
// sysnb Setgid（gid int）（error error）
// sys Setlogin（name string）（error）
// sysnb Setpgid（pid int，pgid）（error）
// sys Setprivexec（flag int）（error error）
// sysnb Setregid（rgid int，egid int）（error error）
// sysnb Setreuid int，euid int（error error）
// sysnb Setuid（uid int）（error error）
// sys Symlink（path string，link string）（error error）
// sys Sync（）（error error）
// sys Umask（newmask int）（oldmak int）
// sys Unmount（path string，flags int）（error error）
// sys write（fd int，p[]byte）（n int，error）
// sys munmap（addr uintpttr，length uintpttr）（error error）
// sysnb fork（）（pid int，error error）
// sysnb ioctl（fd int，req int，arg int）（error error）
// sysnb execve（path*byte，argv**byte，envp byte）（error）
// sysctl（mib[]_C_int，old*byte，oldlen*uintpttr，new*byte，newlen uintpttr）（error error）
// sys fcntlPtr（fd int，cmd int，arg unsafe.Pointer）（val int，error error）=sys fcntl 
// sys unlinkat（fd int，path string，flags，flags int，error）
// sys getcwd（buf[]字节）（n int，error error）

func init() {
	execveDarwin = execve
}

func fdopendir(fd int) (dir uintptr, err error) {
	r0, _, e1 := syscallPtr(abi.FuncPCABI0(libc_fdopendir_trampoline), uintptr(fd), 0, 0)
	dir = uintptr(r0)
	if e1 != 0 {
		err = errnoErr(e1)
	}
	return
}

func libc_fdopendir_trampoline()

// go:cgo_import_dynamic libc_fdopendir fdopendir”/usr/libSystem.B.dylib“

func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
	r0, _, e1 := syscall(abi.FuncPCABI0(libc_read_trampoline), uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
	n = int(r0)
	if e1 != 0 {
		err = errnoErr(e1)
	}
	return
}

func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
	r0, _, e1 := syscall(abi.FuncPCABI0(libc_write_trampoline), uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
	n = int(r0)
	if e1 != 0 {
		err = errnoErr(e1)
	}
	return
}

func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
	// 使用fdopendir/readdir_r/closedir模拟Getdirentries。
	// 我们存储要在查找中跳过的条目数
	// fd的偏移量。见第31368期。
	// 这不是所需的全部语义，但应该能够处理重复调用Getdirentries或ReadDirent的情况。
	// 它不会处理将lseek的结果分配给*basep的操作，也不会处理正在编辑的目录。
	skip, err := Seek(fd, 0, 1 /* SEEK_CUR */)
	if err != nil {
		return 0, err
	}

	// 我们需要复制传入文件描述符
	// 因为调用方希望保留对它的控制，但
	// fdopendir希望控制它的参数。
	// 仅仅复制文件描述符是不够的，因为
	// 结果共享底层状态。使用openat创建一个真正的
	// 新文件描述符，该描述符引用同一目录。
	fd2, err := openat(fd, ".", O_RDONLY, 0)
	if err != nil {
		return 0, err
	}
	d, err := fdopendir(fd2)
	if err != nil {
		Close(fd2)
		return 0, err
	}
	defer closedir(d)

	var cnt int64
	for {
		var entry Dirent
		var entryp *Dirent
		e := readdir_r(d, &entry, &entryp)
		if e != 0 {
			return n, errnoErr(e)
		}
		if entryp == nil {
			break
		}
		if skip > 0 {
			skip--
			cnt++
			continue
		}
		reclen := int(entry.Reclen)
		if reclen > len(buf) {
			// 空间不够。现在就回来。
			// 柜台会告诉我们应该在哪里重新启动。ABCFDG＊＊＊＊＊＊＊/注：此策略在中间暂停和ABCFDG＊＊＊＊＊＊＊/重新启动是在目录长度（o）（n ^ 2）。哦，好吧。
			break
		}
		// 将条目复制到返回缓冲区。
		s := struct {
			ptr unsafe.Pointer
			siz int
			cap int
		}{ptr: unsafe.Pointer(&entry), siz: reclen, cap: reclen}
		copy(buf, *(*[]byte)(unsafe.Pointer(&s)))
		buf = buf[reclen:]
		n += reclen
		cnt++
	}
	// 设置输入fd的搜索偏移量以记录
	// 我们已经返回了多少文件。
	_, err = Seek(fd, cnt, 0 /* SEEK_SET */)
	if err != nil {
		return n, err
	}

	return n, nil
}

// 在运行时包（runtime/sys_darwin.go）
func syscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
func syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func rawSyscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
func rawSyscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func syscallPtr(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
